package org.bhf.security.util;

import org.bhf.security.authorization.RoleBasedSecurityManager;

import javax.security.auth.Subject;
import java.security.Permission;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;

/**
 * Experimental utility to create where clauses from a set of permissions that follow <code>FilePermssion</code>
 * name semantics. It is assumed that redundant permission have already been optimized out.
 * @exclude
 */
public class QueryUtil
{
    private Collection<Permission>  grants = new ArrayList<Permission>( 32 );

    /**
     *
     * @param subject if <code>null</code> use the <code>ContextSubject</code>
     * @param roleBasedSecurityManager Security manager
     * @param permission Must not be <code>null</code>. Used to get the class for filtering the relevant permissions
     *      and to verifying the action. Must have only a single action.
     */
    public QueryUtil( final Subject subject, final RoleBasedSecurityManager roleBasedSecurityManager, final Permission permission )
    {
        final RoleBasedSecurityManager.SubjectPermissions sp = roleBasedSecurityManager.getPermissions( subject, permission );

        {
            final Enumeration<Permission> i = sp.permissions.elements();
            while( i.hasMoreElements() )
            {
                final Permission p = i.nextElement();
                if( p.getActions().contains( permission.getActions() ) )
                    addPermission( p );
            }
        }

        {
            for( int j = 0; j < sp.principalPermissions.length; j++ )
            {
                final Enumeration<Permission> i = sp.principalPermissions[j].permissions.elements();
                while( i.hasMoreElements() )
                {
                    final Permission p = i.nextElement();
                    if( p.getActions().contains( permission.getActions() ) )
                        addPermission( p );
                }
            }
        }
    }

    /**
     * Add a granted <code>Permission</code>.
     * @param p A granted <code>Permission</code>
     */
    public void     addPermission( final Permission p )
    {
        grants.add( p );
    }

    /**
     * Names of the form /foo/- become <code>like</code>s and names of the form /foo/* become =. This works only for
     * selecting based on the parent node of a resource, which is faster than looking at the resource itself.
     * @param column Column name to use for comparison.
     * @return Does not include the 'where' keyword or external parens. A list of OR'd like and <code>=</code> clauses.
     *      if <code>null</code>, no where clause is needed (an '<<ALL>>' was found). if "" (empty), no where clause
     *      is needed because there were no relevant permission granted.
     */
    public String   buildWhereClauseForParent( final String column )
    {
        final StringBuffer sb = new StringBuffer();

        for( final Permission p : grants )
        {
            final String path = p.getName();

            if( "<<ALL>>".equals( path ) )
                return null;

            sb.append( column );
            if( path.endsWith( "/*" ) )
            {
                sb.append( " = '" );
                sb.append( path );
                sb.setLength( sb.length() - 1 );
                sb.append( "'" );
            }
            else if( path.endsWith( "/-" ) )
            {
                sb.append( " like '" );
                sb.append( path );
                sb.setLength( sb.length() - 1 );
                sb.append( "%'" );
            }
            else
            {
                throw new RuntimeException( "Role parameter " + path + " does not end in a /- or /*" );
            }

            sb.append( " or ");
        }

        if( sb.length() > 0 )
        {
            sb.setLength( sb.length() - 4 );
        }

        return sb.toString();
    }
}
